#ifndef __CACHE_H__
#define __CACHE_H__

#include "config.h"
#include "queue.h"
#include <vector>

extern unsigned long cur_cyc;

class Cache {
public:
  Cache(int size, int block_size, int latency, int way, int id);
  int id;
  // return 1 on hit and 0 on miss
  bool Read(unsigned long addr, int requester);
  bool Write(unsigned long addr, int requester);
  // GetLRU returns index in blocks array
  Block* GetVictim(unsigned long addr);
  int GetLatency() {return latency;}
  int GetIndex(unsigned long addr);
  int GetTag(unsigned long addr);
  void FillBlock(unsigned long addr, Cmd cmd, int requester, Block* block);
  bool SameTagIndex(unsigned long addr1, unsigned long addr2);
  Block* findBlock(unsigned long addr, int requester);

//private:
  int size;
  int block_size;
  int latency;
  int way;
  int block_num;

  Block* blocks;
};


class L2Cache {
public:
  Queue* Q;
  vector<QueueElement> waitQ;

  unsigned long mem_req;
  unsigned long mem_rsp;

  L2Cache(int size, int block_size, int latency, int way, int banks, int id, int minL1, int maxL1);
  int GetBank (unsigned long addr);
  void Update();
  void SetMem(Queue* _MemQ) {MemQ = _MemQ;}
  void SetL1(int L1id, Queue* _L1Q) {L1Q[L1id-minL1] = _L1Q;}
  bool QEmpty();
  void PrintStats();

//private:
  Queue* MemQ;
  Queue** L1Q;
  Cache** banks;
  int bank_count;
  int minL1;

  // return 1 on request handled and req can be deleted from the queue
  bool FreshReqHandle(unsigned long addr, int requester, Cmd cmd);
  // will always succeed
  void MissReqHandle(unsigned long addr, int requester, Cmd cmd);
  void CoalascingHandle(unsigned long addr, int requester);

  // Stats
  unsigned long hit;
  unsigned long miss;
  unsigned long wb;
  unsigned long total;
  unsigned long inclusion_evict;
};

class L1Cache {
public:
  Queue* Q;
  vector<QueueElement> waitQ;

  L1Cache(int size, int block_size, int latency, int way, int id);
  void Update();
  int GetLatency() {return cache->GetLatency();}
  void SetL2(Queue* _L2Q) {L2Q = _L2Q;}
  bool QEmpty();
  void PrintStats();
//private:
  Queue* L2Q;
  Cache* cache;
  // return 1 on request handled and req can be deleted from the queue
  bool FreshReqHandle(unsigned long addr, int requester, Cmd cmd);
  // will always succeed
  void MissReqHandle(unsigned long addr, Cmd cmd);
  void CoalascingHandle(unsigned long addr);

  // Stats
  unsigned long hit;
  unsigned long miss;
  unsigned long wb;
  unsigned long total;
  unsigned long access_time;
};


class Memory {
  public:
    Queue* Q;
    Memory(int _L2_count, int _min_L2_id, int _id);
    void SetL2(int L2_id, Queue* L2Q);
    void Update();
    bool QEmpty() {return Q->Empty();}
    int rsp_count;
    int L2_count;
  private:
    int min_L2_id;
    int id;
    Queue** L2Q;
};


#endif
